package com.piece.core.mongo.service;

import com.piece.core.framework.constant.ScheduleConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.util.math.SequenceUtil;
import com.piece.core.mongo.entity.MongoJobLog;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.framework.util.schedule.ScheduleJob;
import com.piece.core.framework.util.schedule.ScheduleRunnable;
import com.piece.core.framework.util.schedule.ScheduleService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * 定时任务实现类-mongo
 */
@Slf4j
@Component("schedule_mongo")
public class MongoScheduleServiceImpl implements ScheduleService {

    @Resource
    private MongoJobLogService mongoJobLogService;

    @Async
    @Override
    public void executeInternal(JobExecutionContext context, ExecutorService service) throws JobExecutionException {
        ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get(ScheduleConstants.JOB_PARAM_KEY);
        int retireCount  = context.getRefireCount();

        // 创建日志
        MongoJobLog jobLog = new MongoJobLog();
        jobLog.setLogId(SequenceUtil.uuid());
        jobLog.setJobName(scheduleJob.getName());
        jobLog.setJobGroup(scheduleJob.getKey());
        jobLog.setMethodName(scheduleJob.getTarget().getClass().getSimpleName() + "[" + scheduleJob.getMethod() + "]");
        jobLog.setMethodParam(scheduleJob.getParam());
        jobLog.setStartTime(new Date());

        Throwable exception = null;
        try {
            log.info("定时任务({}-{})准备)第{}次执行", scheduleJob.getName(), scheduleJob.getKey(), retireCount + 1);
            // 记录日志
            ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getTarget(), scheduleJob.getMethod(), scheduleJob);
            jobLog.setStartTime(new Date());
            jobLog.setStatus(StatusType.RUN.getCode());
            jobLog.setExecCount(retireCount + 1);
            jobLog = mongoJobLogService.insert(jobLog);
            scheduleJob.setLogId(jobLog.getLogId());
            // 执行任务
            Future<?> future = service.submit(task);
            future.get();
        } catch (Exception e) {
            exception = e;
            log.error("定时任务({}-{})第{}次执行失败：{}", scheduleJob.getName(), scheduleJob.getKey(), jobLog.getExecCount(), e.getMessage());
        }

        // 更新日志信息
        updateLogStatus(jobLog, exception);

        handleException(exception, scheduleJob, retireCount);

        log.info("定时任务({}-{})执行完毕,总共耗时：{}毫秒", jobLog.getJobName(), jobLog.getJobGroup(), jobLog.getExecTime());
    }

    private void updateLogStatus(MongoJobLog jobLog, Throwable exception) {
        // 记录日志
        try {
            Date endTime = new Date();
            jobLog.setEndTime(endTime);
            String execTime = DateUtil.getDescriptionTime(endTime.getTime() - jobLog.getStartTime().getTime());
            jobLog.setExceptionInfo(null != exception ? exception.getMessage() : null);
            jobLog.setStatus(null != exception ? StatusType.ERROR.getCode() : StatusType.FINISH.getCode());
            jobLog.setExecTime(execTime);
            mongoJobLogService.update(jobLog);
        } catch (Exception e) {
            log.error("记录定时任务({})执行日志({})失败：{}", jobLog.getJobName(), jobLog.getLogId(), e.getMessage());
        }
    }

    private void handleException(Throwable exception, ScheduleJob scheduleJob, int retireCount) throws JobExecutionException {
        if (null == exception) {
            return;
        }

        if (retireCount > scheduleJob.getRetryCount()) {
            throw new JobExecutionException(exception);
        }

        if (scheduleJob.getRetryInterval() > 0) {
            try {
                Thread.sleep(scheduleJob.getRetryInterval());
            } catch (InterruptedException var3) {
                return;
            }
        }

        // 立即重试
        throw new JobExecutionException(exception, true);
    }
}